﻿using Sidvall.AuthorizationManager.Data;
using Sidvall.Security;
using System.Security.Principal;
using System.Threading.Tasks;

namespace Sidvall.AuthorizationManager.Security
{
    public class AuthorizationProvider : IAuthorizationProvider, IUserProvider
    {
        #region Public Members

        public long? AuthorizationScopeId { get; set; }
        public UserValidatorCollection UserValidators { get; private set; }
        public UserValidationResultCachePolicy UserValidationResultCachePolicy { get; set; }
        #region AuthorizationScopeItem

        private AuthorizationScopeItem _AuthorizationScopeItem;
        public AuthorizationScopeItem AuthorizationScopeItem
        {
            get
            {
                return _AuthorizationScopeItem;
            }
            private set
            {
                if (value != null)
                    AuthorizationScopeItemManager.EnsureValid(value);
                _AuthorizationScopeItem = value;
            }
        }

        #endregion

        #region GetAllResourceItemsAsync

        public async Task<Sidvall.Security.ResourceItemCollection> GetSystemResourceItemsAsync()
        {
            var authorizationScopeItem = await GetAuthorizationScopeItemResolvedAsync().ConfigureAwait(false);
            if (authorizationScopeItem == null)
                return new Sidvall.Security.ResourceItemCollection();
            return await authorizationScopeItem.GetSystemResourceItemsAsync().ConfigureAwait(false);
        }

        #endregion
        #region GetResourceItemsAsync

        public async Task<Sidvall.Security.ResourceItemCollection> GetUserResourceItemsAsync(IPrincipal principal)
        {
            var result = await this.UserValidators.GetUserValidationResultAsync(principal).ConfigureAwait(false);
            var authorizationScopeItem = await GetAuthorizationScopeItemResolvedAsync().ConfigureAwait(false);
            if (authorizationScopeItem == null)
                return new Sidvall.Security.ResourceItemCollection();
            return await authorizationScopeItem.GetUserResourceItemsAsync(principal, result).ConfigureAwait(false);
        }

        #endregion
        #region GetAuthorizeResult

        public AuthorizeResult GetAuthorizeResult(IPrincipal principal, Sidvall.Security.ResourceItemCollection items)
        {
            if (this.AuthorizationScopeItem == null)
            {
                return new AuthorizeResult()
                {
                    UserValidationResult = null,
                    IsAuthorized = false,
                };
            }
            return new AuthorizeResult()
            {
                UserValidationResult = null,
                IsAuthorized = this.AuthorizationScopeItem.IsAuthorized(principal, items),
            };
        }

        #endregion
        #region GetAuthorizeResultAsync

        public async Task<AuthorizeResult> GetAuthorizeResultAsync(IPrincipal principal, Sidvall.Security.ResourceItemCollection items)
        {
            var authorizationScopeItem = await GetAuthorizationScopeItemResolvedAsync().ConfigureAwait(false);
            if (authorizationScopeItem == null)
            {
                return new AuthorizeResult()
                {
                    UserValidationResult = null,
                    IsAuthorized = false,
                };
            }
            var result = await this.UserValidators.GetUserValidationResultAsync(principal).ConfigureAwait(false);
            var isAuthorized = await authorizationScopeItem.IsAuthorizedAsync(principal, result, items).ConfigureAwait(false);
            return new AuthorizeResult()
            {
                UserValidationResult = result,
                IsAuthorized = isAuthorized,
            };
        }

        #endregion

        #region GetUserItemAsync

        public async Task<UserItem> GetUserItemAsync(string name)
        {
            var result = await this.UserValidators.GetUserValidationResultAsync(name).ConfigureAwait(false);
            var authorizationScopeItem = await GetAuthorizationScopeItemResolvedAsync().ConfigureAwait(false);
            if (authorizationScopeItem == null)
                return null;
            return await authorizationScopeItem.GetUserItemAsync(name, result).ConfigureAwait(false);
        }

        #endregion
        #region SaveUserItemAsync

        public async Task<UserItem> SaveUserItemAsync(UserItem item)
        {
            var authorizationScopeItem = await GetAuthorizationScopeItemResolvedAsync().ConfigureAwait(false);
            if (authorizationScopeItem == null)
                return null;
            return await authorizationScopeItem.SaveUserItemAsync(item).ConfigureAwait(false);
        }

        #endregion

        #region LoadFromAuthorizationScope

        public void LoadFromAuthorizationScope(IAuthorizationScope item, bool includeResourceItems, bool includeUserItems)
        {
            this.AuthorizationScopeItem = AuthorizationScopeItemManager.CreateNewFromAuthorizationScope(item, includeResourceItems, includeUserItems);
        }

        #endregion
        #region LoadFromAuthorizationScopeIdAsync

        public async Task LoadFromAuthorizationScopeIdAsync(long authorizationScopeId, AuthorizationScopeCachePolicy cachePolicy)
        {
            switch (cachePolicy)
            {
                case AuthorizationScopeCachePolicy.Resources:
                    this.AuthorizationScopeItem = await GetAuthorizationScopeItemAsync(authorizationScopeId, false, false).ConfigureAwait(false);
                    break;
                case AuthorizationScopeCachePolicy.ResourceItems:
                    this.AuthorizationScopeItem = await GetAuthorizationScopeItemAsync(authorizationScopeId, true, false).ConfigureAwait(false);
                    break;
                case AuthorizationScopeCachePolicy.Users:
                    this.AuthorizationScopeItem = await GetAuthorizationScopeItemAsync(authorizationScopeId, false, true).ConfigureAwait(false);
                    break;
                case AuthorizationScopeCachePolicy.All:
                    this.AuthorizationScopeItem = await GetAuthorizationScopeItemAsync(authorizationScopeId, true, true).ConfigureAwait(false);
                    break;
                default:
                    this.AuthorizationScopeId = authorizationScopeId;
                    break;
            }
        }

        #endregion
        #region LoadFromJson

        public void LoadFromJson(string json)
        {
            this.AuthorizationScopeItem = AuthorizationScopeItemManager.CreateNewFromJson(json);
        }

        #endregion

        #endregion
        #region Private Members

        #region GetAuthorizationScopeItemAsync

        private async Task<AuthorizationScopeItem> GetAuthorizationScopeItemAsync(long authorizationScopeId, bool includeResourceItems, bool includeUserItems)
        {
            var item = await Sidvall.AuthorizationManager.SystemContext.Current.DataPortal.AuthorizationScopeManager.GetItemByPrimaryKeyAsync(null, authorizationScopeId, true,
                includeResourceItems, includeUserItems).ConfigureAwait(false);
            return AuthorizationScopeItemManager.CreateNewFromAuthorizationScope(item, includeResourceItems, includeUserItems);
        }

        #endregion
        #region GetAuthorizationScopeItemResolvedAsync

        private async Task<AuthorizationScopeItem> GetAuthorizationScopeItemResolvedAsync()
        {
            if (this.AuthorizationScopeItem != null)
                return this.AuthorizationScopeItem;
            if (this.AuthorizationScopeId != null)
                return await GetAuthorizationScopeItemAsync(this.AuthorizationScopeId.Value, false, false).ConfigureAwait(false);
            return null;
        }

        #endregion

        #endregion
        #region Constructors

        public AuthorizationProvider()
        {
            this.UserValidators = new UserValidatorCollection();
        }

        #endregion
    }
}
